package util

import (
	"go.uber.org/zap/buffer"
	"time"
)

const startSuffix = "#start"
const endSuffix = "#end"

//StopWatch  record  time cost
type StopWatch struct {
	prefix string
	suffix string
	split  string

	start time.Time
	end   time.Time

	itemName     string
	itemNameList []string
	itemTime     map[string]time.Time
}

//NewDefaultStopWatcher new a stopwatch instance with default config
func NewDefaultStopWatcher() *StopWatch {
	return NewStopWatcher("[", "]", ",")
}

//NewStopWatcher new a stopwatch instance
func NewStopWatcher(prefix, suffix, split string) *StopWatch {
	return &StopWatch{
		prefix:       prefix,
		suffix:       suffix,
		split:        split,
		itemName:     "",
		itemNameList: make([]string, 0),
		itemTime:     make(map[string]time.Time),
	}
}

//Start start a new record
func (sw *StopWatch) Start(itemName string) {
	if len(sw.itemNameList) == 0 {
		sw.start = time.Now()
	}
	sw.itemName = itemName
	sw.itemNameList = append(sw.itemNameList, itemName)
	sw.itemTime[itemName+startSuffix] = time.Now()
}

//Stop stop curr record
func (sw *StopWatch) Stop() {
	stopTime := time.Now()
	sw.end = stopTime
	sw.itemTime[sw.itemName+endSuffix] = stopTime
}

//StopAndStart stop curr record and start a new record
func (sw *StopWatch) StopLastAndStartNew(newItemName string) {
	sw.Stop()
	sw.Start(newItemName)
}

//Result get result string
func (sw *StopWatch) Result() string {
	return sw.String()
}

//String get result string
func (sw *StopWatch) String() string {
	resultBuffer := buffer.Buffer{}
	resultBuffer.AppendString(sw.prefix)
	split := ""
	for _, itemName := range sw.itemNameList {
		resultBuffer.AppendString(split)

		resultBuffer.AppendString(itemName)
		resultBuffer.AppendString(":")
		resultBuffer.AppendString(sw.getTimeCost(itemName))
		split = sw.split
	}

	resultBuffer.AppendString(split)
	resultBuffer.AppendString("total")
	resultBuffer.AppendString(":")
	resultBuffer.AppendString(sw.getTotalCost())

	resultBuffer.AppendString(sw.suffix)
	return resultBuffer.String()
}

func (sw *StopWatch) getTimeCost(itemName string) string {
	start, ok := sw.itemTime[itemName+startSuffix]
	if !ok {
		return "start time not found " + itemName
	}
	end, ok := sw.itemTime[itemName+endSuffix]
	if !ok {
		return "end time not found " + itemName
	}
	return end.Sub(start).String()
}

func (sw *StopWatch) getTotalCost() string {
	return sw.end.Sub(sw.start).String()
}
